iT邦幫忙

2025 iThome 鐵人賽

DAY 4
0
Rust

30天Rust從零到全端系列 第 4

Day 4: 函式與程式流程控制

  • 分享至 

  • xImage
  •  

前言

Hi 大家,昨天我們掌握了 Rust 的變數和資料型別,今天我們來看看如何透過函式來組織程式碼,以及掌握程式的流程控制。函式是程式模組化的基礎,而流程控制則決定了程式的執行邏輯。

函式定義與呼叫

基本函式語法

在 Rust 中,使用 fn 關鍵字定義函式:

fn main() {
    println!("Hello, world!");
    
    // 呼叫自定義函式
    say_hello();
    greet("小明");
}

fn say_hello() {
    println!("你好!");
}

fn greet(name: &str) {
    println!("你好,{}!", name);
}

函式參數

函式參數必須指定型別:

fn main() {
    let result = add(5, 3);
    println!("5 + 3 = {}", result);
    
    let info = create_user_info("張小華", 25);
    println!("{}", info);
}

fn add(x: i32, y: i32) -> i32 {
    x + y  // 注意:沒有分號,這是回傳值
}

fn create_user_info(name: &str, age: u32) -> String {
    format!("用戶:{},年齡:{} 歲", name, age)
}

回傳值

Rust 函式的回傳值有兩種方式:

fn main() {
    println!("明確回傳: {}", explicit_return(10));
    println!("隱含回傳: {}", implicit_return(10));
    
    print_number(42); // 無回傳值的函式
}

// 明確使用 return 關鍵字
fn explicit_return(x: i32) -> i32 {
    if x > 0 {
        return x * 2;  // 提前回傳
    }
    0
}

// 隱含回傳(最後一個表達式)
fn implicit_return(x: i32) -> i32 {
    x * 3  // 沒有分號
}

// 無回傳值(單位型別 ())
fn print_number(x: i32) {
    println!("數字是: {}", x);
}

函式作為參數

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    
    let sum = apply_operation(&numbers, add_one);
    println!("加一後的總和: {}", sum);
    
    let sum2 = apply_operation(&numbers, |x| x * 2); // 閉包
    println!("乘二後的總和: {}", sum2);
}

fn add_one(x: i32) -> i32 {
    x + 1
}

fn apply_operation<F>(numbers: &[i32], operation: F) -> i32 
where 
    F: Fn(i32) -> i32 
{
    numbers.iter().map(|&x| operation(x)).sum()
}

條件控制

if 表達式

fn main() {
    let number = 6;
    
    // 基本 if 表達式
    if number % 4 == 0 {
        println!("數字可以被 4 整除");
    } else if number % 3 == 0 {
        println!("數字可以被 3 整除");
    } else if number % 2 == 0 {
        println!("數字可以被 2 整除");
    } else {
        println!("數字無法被 4、3 或 2 整除");
    }
    
    // if 作為表達式
    let condition = true;
    let number = if condition { 5 } else { 6 };
    println!("條件決定的數字: {}", number);
    
    // 複雜條件判斷
    let age = 20;
    let is_student = true;
    
    let ticket_price = if age < 12 {
        "兒童票:150元"
    } else if age >= 65 {
        "敬老票:200元"
    } else if is_student {
        "學生票:250元"
    } else {
        "全票:300元"
    };
    
    println!("票價:{}", ticket_price);
}

迴圈控制

loop 無限迴圈

fn main() {
    let mut counter = 0;
    
    let result = loop {
        counter += 1;
        
        if counter == 10 {
            break counter * 2; // 回傳值
        }
        
        println!("計數: {}", counter);
    };
    
    println!("迴圈結果: {}", result);
}

while 條件迴圈

fn main() {
    let mut number = 3;
    
    while number != 0 {
        println!("{}!", number);
        number -= 1;
    }
    
    println!("發射!");
    
    // 使用 while 遍歷集合(不推薦)
    let a = [10, 20, 30, 40, 50];
    let mut index = 0;
    
    while index < 5 {
        println!("陣列值: {}", a[index]);
        index += 1;
    }
}

for 迴圈

fn main() {
    // 遍歷陣列(推薦方式)
    let a = [10, 20, 30, 40, 50];
    
    for element in a {
        println!("值: {}", element);
    }
    
    // 使用範圍
    for number in (1..4).rev() {
        println!("{}!", number);
    }
    println!("發射!");
    
    // 帶索引的遍歷
    let fruits = ["蘋果", "香蕉", "橘子"];
    for (index, fruit) in fruits.iter().enumerate() {
        println!("第 {} 個水果: {}", index + 1, fruit);
    }
    
    // 巢狀迴圈與標籤
    'outer: for i in 0..3 {
        for j in 0..3 {
            if i == 1 && j == 1 {
                break 'outer; // 跳出外層迴圈
            }
            println!("i: {}, j: {}", i, j);
        }
    }
}

模式比對(match)

基本 match 語法

fn main() {
    let dice_roll = 6;
    
    match dice_roll {
        1 => println!("得到 1 點!"),
        2 | 3 => println!("得到 2 或 3 點!"), // 多個值
        4..=6 => println!("得到 4 到 6 點!"), // 範圍
        _ => println!("其他點數"), // 預設情況
    }
    
    // match 作為表達式
    let message = match dice_roll {
        1 => "很小",
        2..=3 => "小",
        4..=5 => "中",
        6 => "大",
        _ => "未知",
    };
    
    println!("骰子結果: {}", message);
}

進階 match 模式

fn main() {
    // 匹配元組
    let point = (2, 3);
    
    match point {
        (0, 0) => println!("原點"),
        (0, y) => println!("在 Y 軸上,y = {}", y),
        (x, 0) => println!("在 X 軸上,x = {}", x),
        (x, y) => println!("座標: ({}, {})", x, y),
    }
    
    // 條件守衛
    let number = Some(4);
    
    match number {
        Some(x) if x < 5 => println!("小於 5: {}", x),
        Some(x) => println!("大於等於 5: {}", x),
        None => println!("沒有值"),
    }
    
    // 綁定變數
    let age = 25;
    match age {
        n @ 13..=19 => println!("青少年,年齡: {}", n),
        n @ 20..=59 => println!("成年人,年齡: {}", n),
        n if n >= 60 => println!("長者,年齡: {}", n),
        _ => println!("兒童"),
    }
}

實際專案:簡單的遊戲系統

讓我們建立一個綜合運用今天所學概念的猜數字遊戲:

use std::io;
use std::cmp::Ordering;

fn main() {
    println!("=== 猜數字遊戲 ===");
    
    let secret_number = generate_secret_number();
    let mut attempts = 0;
    let max_attempts = 7;
    
    loop {
        attempts += 1;
        println!("\n第 {} 次嘗試(剩餘 {} 次)", attempts, max_attempts - attempts + 1);
        
        let guess = get_user_input();
        
        match check_guess(guess, secret_number) {
            GameResult::TooLow => {
                println!("太小了!");
                if attempts >= max_attempts {
                    println!("很可惜,答案是 {}!", secret_number);
                    break;
                }
            }
            GameResult::TooHigh => {
                println!("太大了!");
                if attempts >= max_attempts {
                    println!("很可惜,答案是 {}!", secret_number);
                    break;
                }
            }
            GameResult::Correct => {
                println!("🎉 恭喜!你猜對了!");
                let performance = evaluate_performance(attempts);
                println!("你用了 {} 次就猜中,{}", attempts, performance);
                break;
            }
        }
        
        if attempts >= max_attempts {
            break;
        }
    }
    
    if play_again() {
        main(); // 重新開始遊戲
    } else {
        println!("感謝遊玩!");
    }
}

// 列舉定義遊戲結果
#[derive(Debug)]
enum GameResult {
    TooLow,
    TooHigh,
    Correct,
}

fn generate_secret_number() -> u32 {
    // 簡單的偽隨機數生成(實際專案中應使用 rand crate)
    use std::time::{SystemTime, UNIX_EPOCH};
    let timestamp = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("時間錯誤")
        .as_secs();
    
    ((timestamp % 100) as u32) + 1
}

fn get_user_input() -> u32 {
    loop {
        println!("請輸入你的猜測(1-100):");
        
        let mut input = String::new();
        io::stdin()
            .read_line(&mut input)
            .expect("讀取輸入失敗");
        
        match input.trim().parse() {
            Ok(num) if num >= 1 && num <= 100 => return num,
            Ok(_) => println!("請輸入 1 到 100 之間的數字!"),
            Err(_) => println!("請輸入有效的數字!"),
        }
    }
}

fn check_guess(guess: u32, secret: u32) -> GameResult {
    match guess.cmp(&secret) {
        Ordering::Less => GameResult::TooLow,
        Ordering::Greater => GameResult::TooHigh,
        Ordering::Equal => GameResult::Correct,
    }
}

fn evaluate_performance(attempts: u32) -> &'static str {
    match attempts {
        1 => "天才!一次就中!",
        2..=3 => "太厲害了!",
        4..=5 => "很不錯!",
        6..=7 => "還不錯!",
        _ => "需要多練習!",
    }
}

fn play_again() -> bool {
    loop {
        println!("想再玩一次嗎?(y/n)");
        
        let mut input = String::new();
        io::stdin()
            .read_line(&mut input)
            .expect("讀取輸入失敗");
        
        match input.trim().to_lowercase().as_str() {
            "y" | "yes" | "是" => return true,
            "n" | "no" | "否" => return false,
            _ => println!("請輸入 y 或 n"),
        }
    }
}

總結

今天我們學習了 Rust 的函式系統和程式流程控制,重點包括:

  • 函式定義、參數傳遞和回傳值的處理
  • 條件控制:if 表達式的靈活運用
  • 迴圈控制:loop、while 和 for 的適用場景
  • 模式比對:match 表達式的強大功能
  • 實際應用:完整的猜數字遊戲專案

明天我們將學習 Rust 的所有權系統,這是 Rust 實現記憶體安全的核心機制。


上一篇
Day 3: Rust 基礎語法 - 變數與資料型別
下一篇
Day 5: 控制流程進階與錯誤處理
系列文
30天Rust從零到全端15
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言